﻿using System.IO;
using System.Collections;

using FileWatcher.NodeInfo;

using Blackhole.Debug;

namespace FileWatcher
{
    public class Node
    {
        private bool m_isReady;
        private int m_stability;
        private int m_stabilityCount;
        private bool m_recursive;
        private INodeInfo m_nodeInfo;
        private ArrayList m_childList;
        private string m_acceptPattern;

        public bool IsParent
        {
            get { return this.m_nodeInfo.IsParent; }
        }

        public int ChildCount
        {
            get { return this.m_childList.Count; }
        }

        public string Location
        {
            get { return this.m_nodeInfo.Location; }
        }

        public Node this[int index]
        {
            get { return this.m_childList[index] as Node; }
        }

        public Node(INodeInfo node, bool recursive, int stability)
        {
            this.m_stability = stability;

            this.m_acceptPattern = "";

            this.m_stabilityCount = 0;

            this.m_isReady = false;

            this.m_nodeInfo = node;

            this.m_recursive = recursive;

            if (this.IsParent)
                this.m_childList = new ArrayList();
        }

        public Node(INodeInfo node, bool recursive, int stability, string acceptPattern)
            : this(node, recursive, stability)
        {
            this.m_acceptPattern = acceptPattern;
        }

        public void BuildTree()
        {
            this.BuildTree(false);
        }

        public void BuildTree(bool processExisting)
        {
            if (!this.IsParent) return;

            INodeInfo[] nodes = this.GetNodes();

            foreach (INodeInfo node in nodes)
            {
                Node newNode = new Node(node, this.m_recursive, this.m_stability, this.m_acceptPattern);

                if ((processExisting && node is IFolderNodeInfo) || 
                    (!processExisting))
                {
                    this.m_childList.Add(newNode);
                }

                if (this.m_recursive && node is IFolderNodeInfo)
                    newNode.BuildTree(processExisting);
            }
        }

        public Node GetChanges(ArrayList readyFiles)
        {
            Node newNode = new Node(this.m_nodeInfo, this.m_recursive, this.m_stability, this.m_acceptPattern);

            newNode.BuildTree();

            this.ProcessNodes(newNode, this, readyFiles);

            return newNode;
        }

        private void ProcessNodes(Node newNode, Node oldNode, ArrayList readyFiles)
        {
            if (oldNode == null)
            {
                newNode.UpdateStatus(oldNode, readyFiles);
            }
            else
            {
                if (newNode.IsParent && oldNode.IsParent)
                {
                    int size = newNode.ChildCount;

                    for (int i = 0; i < size; i++)
                    {
                        Node newChild = newNode[i];
                        Node oldChild = oldNode.MatchChild(newChild);

                        this.ProcessNodes(newChild, oldChild, readyFiles);
                    }
                }
                else if (!newNode.IsParent && !oldNode.IsParent)
                {
                    newNode.UpdateStatus(oldNode, readyFiles);
                }
            }
        }

        private void UpdateStatus(Node oldNode, ArrayList readyFiles)
        {
            if (this is IFolderNodeInfo) return;

            if (oldNode == null)
            {
                this.m_isReady = true;

                Garbage.Log(this, new LoggerArguements(LoggerArguements.LoggerLevels.All, "New file found \"{0}\"", this.Location));
            }
            else
            {
                this.CopyStatus(oldNode);

                if (this is IFolderNodeInfo)
                    this.m_isReady = false;

                if (((IFileNodeInfo)this.m_nodeInfo).Modified > ((IFileNodeInfo)oldNode.m_nodeInfo).Modified ||
                    ((IFileNodeInfo)this.m_nodeInfo).Size > ((IFileNodeInfo)oldNode.m_nodeInfo).Size)
                {
                    this.m_stabilityCount = 0;
                }
            }

            if (this.m_isReady)
            {
                if (this.m_stabilityCount < this.m_stability)
                {
                    this.m_stabilityCount++;
                }
                else
                {
                    readyFiles.Add(this);

                    this.m_isReady = false;

                    Garbage.Log(this, new LoggerArguements(LoggerArguements.LoggerLevels.All, "File \"{0}\" ready to be processed", this.m_nodeInfo.Location));
                }
            }
        }

        private void CopyStatus(Node oldNode)
        {
            this.m_stabilityCount = oldNode.m_stabilityCount;

            this.m_isReady = oldNode.m_isReady;

            this.m_acceptPattern = oldNode.m_acceptPattern;
        }

        protected Node MatchChild(Node newNode)
        {
            int size = this.m_childList.Count;

            for (int i = 0; i < size; i++)
            {
                Node childNode = this[i];

                if (childNode.Equals(newNode))
                    return childNode;
            }

            return null;
        }

        public override bool Equals(object obj)
        {
            return (this.Location == ((Node)obj).Location) ? true : false;
        }

        private INodeInfo[] GetNodes()
        {
            int total;
            INodeInfo[] nodes;

            string[] acceptPatterns = this.m_acceptPattern.Split('|');

            string[] directories = Directory.GetDirectories(this.m_nodeInfo.Location);
            string[][] files = new string[acceptPatterns.Length][];

            total = 0;

            for (int i = 0; i < files.Length; i++)
            {
                files[i] = Directory.GetFiles(this.m_nodeInfo.Location, acceptPatterns[i]);

                total += files[i].Length;
            }

            nodes = new INodeInfo[total + directories.Length];

            total = 0;

            for (int i = 0; i < directories.Length; i++, total++)
            {
                nodes[total] = new FolderNodeInfo(directories[i]);
            }

            for (int i = 0; i < files.Length; i++)
            {
                for (int j = 0; j < files[i].Length; j++, total++)
                {
                    nodes[total] = new FileNodeInfo(files[i][j]);
                }
            }

            return nodes;
        }
    }
}